#include "csr.h"
#include "kernel.h"
#include "print.h"

void rv_machine_trap_entry(void);
void rv_supervisor_trap_entry(void);
void sinit(void);

void rv_machine_trap_init(void)
{
    unsigned long status = csr_read(mstatus);

    // 设置M模式中断和异常入口.
    csr_write(mtvec, (unsigned long)rv_machine_trap_entry);

    // 使能M模式全局中断
    status |= MSTATUS_MIE;
    csr_write(mstatus, status);

    // 使能M模式和S模式外部中断
    csr_write(mie, MIE_MEIE | MIE_SEIE);
}

void rv_machine_switchto_supervisor(void)
{
    // 设置MPP为S模式,这样在执行mret之后处理器就切换为S模式.
    unsigned long status = csr_read(mstatus);
    status &= ~MSTATUS_MPP_MASK;
    status |= MSTATUS_MPP_S;
    csr_write(mstatus, status);

    // 设置异常返回地址,mret执行后跳转到此地址执行
    // 需要 gcc -mcmodel=medany 参数
    csr_write(mepc, (unsigned long)sinit);

    // 关闭MMU
    csr_write(satp, 0);

    // 托管异常和中断到S模式处理
    csr_write(medeleg, 0xb1ff);
    csr_write(mideleg, 0x222);

    // 配置物理内存保护,以让S模式访问所有物理内存
    csr_write(pmpaddr0, 0x3fffffffffffffull);
    csr_write(pmpcfg0, 0xf);

    // 切换到S模式.
    asm volatile("mret");
}

void rv_supervisor_trap_init(void)
{
    // 设置S模式中断和异常入口.
    csr_write(stvec, (unsigned long)rv_supervisor_trap_entry);

    // 在sret指令执行时spie的值自动赋值给sie
    // SPP 设置为1,这样sret执行后处理器还是处于 S 模式
    csr_set(sstatus, MSTATUS_SPIE | MSTATUS_SPP);
}

void os_int_enable(unsigned long flag)
{
    csr_write(sstatus, flag);
}

unsigned long os_int_disable(void)
{
    unsigned long flag;

    flag = csr_read(sstatus);
    csr_clear(sstatus, MSTATUS_SIE);
    return flag;
}